package com.gzsxy.utils;

import com.sun.org.apache.bcel.internal.classfile.Constant;
import org.apache.commons.compress.archivers.tar.TarArchiveEntry;
import org.apache.commons.compress.archivers.tar.TarArchiveInputStream;
import org.apache.commons.compress.archivers.tar.TarArchiveOutputStream;
import sun.dc.pr.PRError;

import java.io.*;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;

/**
 * @author xiaolong
 * @version 1.0
 * @description: 多文件压缩与解压
 * @date 2021/10/8 13:43
 */
public class CompressUtil {

    private static final int BUFFER = 1024;

    private static final String PATH = "/";
    private static final String BASE_DIR = "";


    /**
     * @param tar_gz       要解压的文件
     * @param sourceFolder 要解压的目录
     * @return
     * @description: 解压tar.gz文件
     */
    public static void decompress(File tar_gz, String sourceFolder) throws IOException {
        FileInputStream fis = null;
        BufferedInputStream bis = null;
        GZIPInputStream gzis = null;
        TarArchiveInputStream tais = null;
        FileOutputStream fos = null;
        try {
            fis = new FileInputStream(tar_gz);
            bis = new BufferedInputStream(fis);
            gzis = new GZIPInputStream(bis);
            //解包归档输入流
            tais = new TarArchiveInputStream(gzis);
            TarArchiveEntry tae = null;
            //获取归档文件中的条目
            while ((tae = tais.getNextTarEntry()) != null) {
                if (tae.isDirectory()) {
                    continue;
                }
                //getName获取文件名
                File tmpFile = new File(sourceFolder, tae.getName());
                //获取父目录
                File parentFile = tmpFile.getParentFile();
                //创建目录
                if (!parentFile.exists()) {
                    parentFile.mkdirs();
                }
                fos = new FileOutputStream(tmpFile);
                int len;
                byte[] buffer = new byte[BUFFER];
                while ((len = tais.read(buffer)) != -1) {
                    fos.write(buffer, 0, len);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (fos != null) {
                fos.flush();
                fos.close();
            }
            if (tais != null) {
                tais.close();
            }
            if (gzis != null) {
                gzis.close();
            }
            if (bis != null) {
                bis.close();
            }
            if (fis != null) {
                fis.close();
            }
        }
    }


    /**
     * @param sourcePath 要打包压缩的路径
     * @param outPutPath 生成的 .tar.gz文件的输出目录
     * @param fileName   生成的.tar.gz 文件的文件名（不用加文件类型后缀）
     * @return
     * @description: 将指定路径的文件归档、打包、压缩成 .tar.gz格式的文件
     */
    public static File compresser(String sourcePath, String outPutPath, String fileName) throws IOException {
        File tar = null;
        FileInputStream fis = null;
        BufferedInputStream bis = null;
        File outPutFile = null;
        FileOutputStream fos = null;
        GZIPOutputStream gzp = null;
        try {
            //将路径中的所有内容归档成tar包后返回
            tar = pack(sourcePath);
            fis = new FileInputStream(tar);
            bis = new BufferedInputStream(fis);
            outPutFile = new File(outPutPath + "/" + fileName + ".tar.gz");
            fos = new FileOutputStream(outPutFile);
            gzp = new GZIPOutputStream(fos);
            int len;
            byte[] buffer = new byte[BUFFER];
            while ((len = bis.read(buffer)) != -1) {
                gzp.write(buffer, 0, len);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (gzp != null) {
                //将压缩数据写入输出流而不关闭底层流。 连续应用多个过滤器到相同的输出流时使用此方法。
                gzp.finish();
                gzp.flush();
                gzp.close();
            }
            if (fos != null) {
                fos.close();
            }
            if (bis != null) {
                bis.close();
            }
            if (fis != null) {
                fis.close();
            }
            if (tar.exists()) {
                tar.delete();
            }
        }
        return outPutFile;
    }


    /**
     * @param sourcePath 要打包压缩的路径
     * @return
     * @description: 将路径中的所有内容归档成tar包后返回
     */
    private static File pack(String sourcePath) throws Exception {
        File srcFile = new File(sourcePath);
        String name = srcFile.getName();
        String basePath = srcFile.getParent();
        File target = new File(basePath + PATH + name + ".tar");
        //打包归档输出流
        TarArchiveOutputStream taos = new TarArchiveOutputStream(new FileOutputStream(target));
        //解决文件名过长问题
        taos.setLongFileMode(TarArchiveOutputStream.LONGFILE_GNU);
        //根据类型进行归档函数调用
        archive(srcFile, taos, BASE_DIR);
        taos.flush();
        taos.close();
        return target;
    }


    /**
     * @param
     * @return
     * @description: 根据类型进行归档函数调用
     */
    private static void archive(File srcFile, TarArchiveOutputStream taos, String basePath) throws Exception {
        //如果是文件夹
        if (srcFile.isDirectory()) {
            archiveDir(srcFile, taos, basePath);
        } else {
            archiveFile(srcFile, taos, basePath);
        }
    }


    /**
     * @param
     * @return
     * @description: 目录递归归档
     */
    private static void archiveDir(File dir, TarArchiveOutputStream taos, String basePath) throws Exception {
        File[] files = dir.listFiles();
        if (files.length < 1) {
            TarArchiveEntry entry = new TarArchiveEntry(basePath + dir.getName() + PATH);
            //增加打包归档的条目
            taos.putArchiveEntry(entry);
            taos.closeArchiveEntry();
        }
        for (File file : files) {
            archive(file, taos, basePath + dir.getName() + PATH);
        }
    }

    /**
     * @param
     * @return
     * @description: 文件归档
     */
    private static void archiveFile(File file, TarArchiveOutputStream taos, String dir) throws Exception {
        TarArchiveEntry entry = new TarArchiveEntry(dir + file.getName());
        entry.setSize(file.length());
        //增加打包归档的条目
        taos.putArchiveEntry(entry);
        BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file));
        int len;
        byte[] buffer = new byte[BUFFER];
        while ((len = bis.read(buffer)) != -1) {
            taos.write(buffer, 0, len);
        }
        bis.close();
        taos.closeArchiveEntry();
    }


    //测试
    public static void main(String[] args) throws IOException {
//        String path = "D:\\dirA";
//        File file = new File("D:\\dirA\\apache-zookeeper-3.6.3-bin.tar.gz");
//        decompress(file,path);

//        String path = "D:\\dirA\\ceshi";
//        String path1 = "D:\\dirA";
//        String path2 = "cess";
//
//        compresser(path,path1,path2);
    }
}
